<template>
  <div class="wrapper">
    <div class="uploader-hide">
      <input
          id="uploader-input-file"
          type="file"
          multiple
          @change="selectFile"
      >
      <input
          id="uploader-input-folder"
          type="file"
          webkitdirectory
          @change="selectFile"
      >
    </div>
    <div class="task-header">
        <task-header
            v-if="isAllCompleted"
            :status="0"
            :count="uploadSuccess"
        />
        <task-header
            v-else-if="isAllPaused && hasError"
            :status="1"
            :count="errorCount"
        />
        <task-header
            v-else-if="isAllPaused"
            :status="2"
            :count="uploadItem.length"
        />
        <task-header
            v-else
            :status="3"
            :count="uploadItem.length"
        />
    </div>
    <div class="tasks-container">
      <el-card
          shadow="always"
          class="card"
      >
        <div class="content">
          <ul>
            <li
                v-for="file in uploadItem"
                :key="file.identifier"
            >
              <div class="task-info">
                <div class="image">
                  <svg
                      class="iconfont"
                  >
                    <use xlink:href="#icon-cs-other"/>
                  </svg>
                </div>
                <div class="title">
                  {{ file.fileName }}
                </div>
              </div>
              <div class="task-progress">
                <el-progress
                    :percentage="file.progress*100"
                    :status="formatStatus(file)"
                />
              </div>
              <div
                  class="operation-button"
                  v-if="file.status !== 'success'"
              >
                <el-popconfirm
                    title="确认取消下载吗？"
                    @confirm="cancel(file.identifier)"
                    trigger="click"
                >
                  <template #reference>
                    <el-link
                        type="danger"
                        :underline="false"
                        href="javascript:void(0)"
                    >
                      取消
                    </el-link>
                  </template>
                </el-popconfirm>
                <el-link
                    type="warning"
                    :underline="false"
                    href="javascript:void(0)"
                    @click.stop="pause(file.identifier)"
                >
                  暂停
                </el-link>
                <el-link
                    type="success"
                    :underline="false"
                    href="javascript:void(0)"
                    @click.stop="resume(file.identifier)"
                >
                  继续
                </el-link>
              </div>
            </li>
          </ul>
        </div>
      </el-card>
    </div>
  </div>
</template>

<script lang="ts">
import {defineComponent} from 'vue'
import {Flow, FlowFile} from 'flowjs'
import {createFlow} from '@/utils/flow'
import '@/utils/array'
import {runCatching} from '@/utils/function'
import { ElMessage } from 'element-plus'
import TaskHeader from '@/views/task/TaskHeader.vue'

export interface UploadItem {
    status: 'uploading' | 'paused' | 'error' | 'success'
    progress: number,
    totalSize: number,
    uploaded: number,
    averageSpeed: number,
    fileName: string,
    identifier: string
}

export default defineComponent({
    name: 'Tasks',
    components: {
        TaskHeader
    },
    data() {
        return {
            flow: {} as Flow,
            totalUpload: 0,
            uploadMap: new Map<string, UploadItem>()
        }
    },
    computed: {
        uploadItem(): Array<UploadItem> {
            return [...this.uploadMap.values()]
        },

        uploadRemain(): number {
            return this.uploadItem.count((it) => it.status !== 'success')
        },
        uploadSuccess(): number {
            return this.totalUpload
        },
        errorCount(): number {
            return this.uploadItem.count((it) => it.status !== 'error')
        },
        pausedCount(): number {
            return this.uploadItem.count((it) => it.status !== 'paused')
        },
        isAllCompleted(): boolean {
            return this.uploadRemain === 0
        },
        isAllPaused(): boolean {
            // loading <= 0
            return this.uploadRemain <= this.errorCount + this.pausedCount
        },
        hasError(): boolean {
            return this.errorCount > 0
        }
    },
    deactivated() {
        this.$bus.off('show-browse')
        this.$bus.off('show-browse-folder')
        this.$bus.off('add-upload-drop')
    },
    activated() {
        this.flow = createFlow({
            target: '/api/file/upload',
            method: 'multipart',
            maxChunkRetries: 5,
            chunkRetryInterval: 1000,
            allowDuplicateUploads: true,
            simultaneousUploads: 5,
            headers: {
                Authorization: `Bearer ${this.$store.state.authToken}`
            },
            query(file: FlowFile) {
                return {
                    userName: localStorage.getItem('userName')!,
                    destinationPath: file.destinationPath
                }
            }
        })
        if(!this.flow.support) {
            return
        }

        // flow.on
        this.flow.on('filesSubmitted', () => {
            runCatching(() => {
                this.flow.upload()
            })
        })

        this.flow.on('fileAdded', (file) => {
            const paths = (this.$store.state.file.destinationPath || []) as Array<string>
            file.destinationPath = paths.join('/')
            // 这里的value没进去
            this.uploadMap.set(file.uniqueIdentifier, {
                identifier: file.uniqueIdentifier,
                fileName: file.name,
                totalSize: file.size,
                status: 'uploading',
                uploaded: 0,
                progress: 0,
                averageSpeed: 0
            })
        })

        this.flow.on('fileRemoved', (file) => {
            this.uploadMap.delete(file.uniqueIdentifier)
        })

        this.flow.on('fileProgress', (file) => {
            if(!this.uploadMap.has(file.uniqueIdentifier)) {
                file.cancel()
            }
            const upload = this.uploadMap.get(file.uniqueIdentifier)!
            const status = upload.status
            if(status === 'uploading' && upload.status !== 'uploading') {
                upload.status = 'uploading'
            }
            upload.progress = file.progress()
            upload.uploaded = file.sizeUploaded()
            upload.averageSpeed = file.averageSpeed
        })

        this.flow.on('fileRetry', (file) => {
            if(this.uploadMap.has(file.uniqueIdentifier)) {
                this.uploadMap.get(file.uniqueIdentifier)!.status = 'uploading'
            }
        })

        this.flow.on('fileError', (file) => {
            if (this.uploadMap.has(file.uniqueIdentifier)) {
                this.uploadMap.get(file.uniqueIdentifier)!.status = 'error'
            }
            ElMessage.error({
                message: `文件 ${file.name} 上传失败，请重试`,
                type: 'error'
            })
        })

        this.flow.on('fileSuccess', (file) => {
            ElMessage.success({
                message: `文件 ${file.name} 上传成功`,
                type: 'success'
            })
            this.totalUpload++
            if(this.uploadMap.has(file.uniqueIdentifier)) {
                this.uploadMap.get(file.uniqueIdentifier)!.status = 'success'
            }
            setTimeout(() => {
                this.cancel(file.uniqueIdentifier)
            }, 20000)
        })

        // bus 上传文件和文件夹 拖拽文件
        this.$bus.on('show-browse', () => {
            document.getElementById('uploader-input-file')!.click()
        })
        this.$bus.on('show-browse-folder', () => {
            document.getElementById('uploader-input-folder')!.click()
        })
        this.$bus.on('add-upload-drop', (event: MouseEvent) => {
            runCatching(() => {
                (this.flow as any).onDrop(event)
            })
        })
    },
    methods: {
        formatStatus(file: UploadItem) {
            if(file.status === 'success') {
                return 'success'
            }
            if(file.status === 'paused') {
                return 'warning'
            }
            if(file.status === 'error') {
                return 'exception'
            }
        },

        getFile(identity: string): FlowFile {
            return this.flow.getFromUniqueIdentifier(identity)
        },


        pause(id: string | null = null) {
            if(id === null) {
                runCatching(() => {
                    this.flow.pause()
                })
                this.uploadMap.forEach((it) => {
                    it.status = 'paused'
                })
                return
            }
            this.getFile(id)?.pause()
            this.uploadMap.get(id)!.status = 'paused'
        },

        resume(id: string | null = null) {
            if(id === null) {
                this.flow.files.forEach((it) => {
                    const upload = this.uploadMap.get(it.uniqueIdentifier)
                    if(upload && upload.status !== 'success') {
                        runCatching(() => {
                            if(it.paused) {
                                it.resume()
                            } else {
                                it.retry()
                            }
                        })
                        upload.status = 'uploading'
                    }
                })
                return
            }
            const upload = this.uploadMap.get(id)
            const file = this.getFile(id)
            if(upload && upload.status !== 'success') {
                runCatching(() => {
                    if(file.paused) {
                        file.resume()
                    } else {
                        file.retry()
                    }
                })
                upload.status = 'uploading'
            }
        },

        cancel(id: string | null = null) {
            if(id === null) {
                runCatching(() => {
                    this.flow.cancel()
                })
                this.uploadMap.clear()
                return
            }
            this.getFile(id)?.cancel()
            this.uploadMap.delete(id)
        },

        selectFile(e: any) {
            if(e.target && e.target.value) {
                const f: any = this.flow
                f.addFiles(e.target.files, e)
                e.target.value = ''
            }
        }
    }
})
</script>

<style scoped lang="less">
.uploader-hide {
  visibility: hidden;
  position: absolute;
  width: 1px;
  height: 1px;
}

.card {
  margin: 10px 0 20px
}

.tasks-container .content {
  display: flex;
  flex-direction: column;
  ul {
    padding: 0;
    margin: 10px;
    li {
      display: flex;
      flex-direction: row;
      position: relative;
    }
  }
}

.task-info {
  width: 30%;
  display: flex;
  .image {
    svg {
      height: 35px;
      width: 35px;
    }
  }
  .title {
    width: 100%;
    display: flex;
    align-items: center;
    margin-left: 20px;
  }
}
.task-progress {
  width: 70%;
  display: flex;
  align-items: center;
}
</style>

<style lang="less">
.tasks-container .el-card__body {
  padding: 0
}
.task-progress .el-progress {
  width: 100%
}
</style>